﻿using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using System.Drawing;
using MySql.Data.MySqlClient;

namespace iLikeLite
{
    class Processor
    {
        private Hashtable keywordHistograms;

        public EventHandler ProcessorDone;

        public string curTitle, curKeywords, curHistogram;
        public Image curImage;

        public ArrayList GetImageIds()
        {
            MySqlConnection conn = new MySqlConnection(Properties.Settings.Default.mydbConnectionString);
            conn.Open();
            MySqlCommand cmd = new MySqlCommand("SELECT id FROM images", conn);
            MySqlDataReader reader = cmd.ExecuteReader();
            ArrayList ids = new ArrayList();
            if (reader.HasRows)
            {
                while (reader.Read())
                {
                    ids.Add(reader.GetInt32("id"));
                }
                reader.Close();
            }
            conn.Close();
            return ids;
        }

        public void GetImage(int id)
        {
            MySqlConnection conn = new MySqlConnection(Properties.Settings.Default.mydbConnectionString);
            conn.Open();
            MySqlCommand cmd = new MySqlCommand("SELECT image, title, keywords, histogram FROM images WHERE id = " + id, conn);
            MySqlDataReader reader = cmd.ExecuteReader();
            if (reader.HasRows)
            {
                reader.Read();
                curImage = Image.FromStream(new MemoryStream(Convert.FromBase64String(reader.GetString("image"))));
                curTitle = reader.GetString("title");
                curKeywords = reader.GetString("keywords");
                curHistogram = "";
                if (!reader.IsDBNull(reader.GetOrdinal("histogram")))
                {
                    curHistogram = reader.GetString("histogram");
                }
            }
            reader.Close();
            conn.Close();
        }

        public void SaveInfo(int id, string title, string keywords)
        {
            MySqlConnection conn = new MySqlConnection(Properties.Settings.Default.mydbConnectionString);
            conn.Open();

            MySqlCommand cmd = new MySqlCommand("SELECT keywords FROM images WHERE id=?i", conn);
            cmd.Parameters.AddWithValue("?i", id);
            MySqlDataReader reader = cmd.ExecuteReader();
            string oldKeywords = keywords;
            if (reader.Read())
            {
                oldKeywords = reader.GetString("keywords");
            }
            reader.Close();

            cmd = new MySqlCommand("UPDATE images SET title=?t WHERE id=?i", conn);
            cmd.Parameters.AddWithValue("?i", id);
            cmd.Parameters.AddWithValue("?t", title);
            cmd.ExecuteNonQuery();

            cmd = new MySqlCommand("UPDATE images SET keywords=?k1 WHERE keywords=?k2", conn);
            cmd.Parameters.AddWithValue("?k1", keywords);
            cmd.Parameters.AddWithValue("?k2", oldKeywords);
            cmd.ExecuteNonQuery();

            conn.Close();
        }

        public void Start()
        {
            Thread t = new Thread(new ThreadStart(StartThreaded));
            t.Start();
        }

        private void StartThreaded()
        {
            ArrayList ids = GetImageIds();
            keywordHistograms = new Hashtable();
            string histogram = "";

            MySqlConnection conn = new MySqlConnection(Properties.Settings.Default.mydbConnectionString);
            conn.Open();
            MySqlCommand cmd;
            MySqlDataReader reader;
            foreach (int id in ids)
            {
                histogram = "";
                cmd = new MySqlCommand("SELECT image, title, keywords FROM images WHERE id = " + id, conn);
                reader = cmd.ExecuteReader();
                if (reader.Read())
                {
                    Image img = Image.FromStream(new MemoryStream(Convert.FromBase64String(reader.GetString("image"))));
                    histogram = GetHistogram(img);
                    AddKeywordHistogram(reader.GetString("keywords"), histogram);
                }
                reader.Close();

                string query = "UPDATE images SET histogram=?hg WHERE id=" + id;

                cmd = new MySqlCommand(query, conn);
                cmd.Parameters.AddWithValue("?hg", histogram);
                cmd.ExecuteNonQuery();
            }
            

            NormalizeKeywordHistograms();
            foreach (string keyword in keywordHistograms.Keys)
            {
                string histogramStr = "";
                double[] histogramArr = (double[])keywordHistograms[keyword];
                for (int i = 0; i < histogramArr.Length; ++i)
                {
                    histogramStr += histogramArr[i].ToString("#0.##") + ",";
                }

                cmd = new MySqlCommand("SELECT * FROM keywords WHERE keyword=?k", conn);
                cmd.Parameters.AddWithValue("?k", keyword);
                reader = cmd.ExecuteReader();
                if (reader.HasRows)
                {
                    reader.Close();
                    string query = "UPDATE keywords SET histogram=?hg WHERE keyword=?k";

                    cmd = new MySqlCommand(query, conn);
                    cmd.Parameters.AddWithValue("?k", keyword);
                    cmd.Parameters.AddWithValue("?hg", histogramStr.Substring(0, histogramStr.Length - 1));
                    cmd.ExecuteNonQuery();
                }
                else
                {
                    reader.Close();
                    string query = "INSERT INTO keywords (keyword, histogram) VALUES (?k, ?hg)";

                    cmd = new MySqlCommand(query, conn);
                    cmd.Parameters.AddWithValue("?k", keyword);
                    cmd.Parameters.AddWithValue("?hg", histogramStr.Substring(0, histogramStr.Length - 1));
                    cmd.ExecuteNonQuery();
                }
                
            }

            conn.Close();

            if (ProcessorDone != null)
            {
                ProcessorDone(null, EventArgs.Empty);
            }
        }

        private string GetHistogram(Image img)
        {
            Bitmap bmp = new Bitmap((Image)img.Clone());
            int binCount = 8;
            int[, ,] histogram = new int[binCount, binCount, binCount];
            int numPixels = 0;
            for (int y = 0; y < bmp.Height; ++y)
            {
                for (int x = 0; x < bmp.Width; ++x)
                {
                    Color px = bmp.GetPixel(x, y);

                    //ignore white pixels
                    if (!(px.R > 253 && px.G > 253 && px.B > 253))
                    {
                        histogram[GetColorBin(px.R), GetColorBin(px.G), GetColorBin(px.B)] += 1;
                        ++numPixels;
                    }
                }
            }

            string histogramStr = "";
            //double total = binCount;
            for (int i = 0; i < binCount; ++i)
            {
                for (int j = 0; j < binCount; ++j)
                {
                    for (int k = 0; k < binCount; ++k)
                    {
                        double val = (histogram[i, j, k] * 100.0 / (double)numPixels);
                        histogramStr += val.ToString("#0.##") + ",";
                        //total += val;
                    }
                }
            }
            return histogramStr.Substring(0, histogramStr.Length - 1);
        }

        private void AddKeywordHistogram(string imageKeywords, string imageHistogram)
        {
            string[] keywords = imageKeywords.ToLower().Split(' ');
            string[] histoStrs = imageHistogram.Split(',');
            double[] histogram = new double[histoStrs.Length];
            for (int i = 0; i < histogram.Length; ++i)
            {
                histogram[i] = Convert.ToDouble(histoStrs[i]);
            }
            
            foreach (string keyword in keywords)
            {
                if (keywordHistograms.ContainsKey(keyword))
                {
                    double[] keywordHistogram = (double[])keywordHistograms[keyword];
                    for (int i = 0; i < keywordHistogram.Length; ++i)
                    {
                        //ignore small traces of color
                        if (histogram[i] > 2.0)
                        {
                            keywordHistogram[i] += histogram[i];
                        }
                    }
                }
                else
                {
                    keywordHistograms[keyword] = histogram;
                }
            }
        }

        private void NormalizeKeywordHistograms()
        {
            foreach (string keyword in keywordHistograms.Keys)
            {
                double total = 0;
                double[] histogram = (double[])keywordHistograms[keyword];
                for (int i = 0; i < histogram.Length; ++i)
                {
                    total += histogram[i];
                }
                for (int i = 0; i < histogram.Length; ++i)
                {
                    double nVal = (histogram[i] * 100) / total;
                    if (nVal < 2.0)
                    {
                        total -= histogram[i];
                        histogram[i] = 0;
                    }
                }
                for (int i = 0; i < histogram.Length; ++i)
                {
                    histogram[i] = (histogram[i] * 100) / total;
                }
            }
        }

        private int GetColorBin(int colorVal)
        {
            return colorVal / 32;
        }
    }
}
